package edu.cmu.javadb;

import java.util.*;

import edu.cmu.javadb.exceptions.NotFlattenableException;
import edu.cmu.javadb.exceptions.NotProjectableException;
import edu.cmu.javadb.plan.*;
import edu.cmu.javadb.plan.condition.*;

public class View<T> implements Iterable<T>, Queryable<T>, Cloneable {
	protected PlanNode plan;
	
	public View(PlanNode plan) {
		this.plan = plan;
	}

	// public MView<T> materialize() {
	// MView<T> mv = new MView<T>(collection);
	//
	// collection.children.add(mv);
	// return mv;
	// }

	@Override
	public Collection<T> execute() {
		return (Collection<T>) Engine.getInstance().execute(plan);
	}

	@Override
	public Iterator<T> iterator() {
		return execute().iterator();
	}

	@Override
	public View<T> filter(Condition cond) {
		View<T> res = new View<T>(new Selection(plan, cond));
		return res;
	}

	@Override
	public <T2> Queryable<T2> project(Path path) throws NotProjectableException {
		View<T2> res = new View<T2>(new Projection(plan, Arrays.asList(path)));
		return res;
	}

	@Override
	public <T2> Queryable<T> join(Queryable<T2> c2, Path p1, Path p2) {
		View<T> res = new View<T>(new Join(plan, c2.getPlan()));
		return res;
	}

	@Override
	public <T2> Queryable<T2> flatten() throws NotFlattenableException {
		View<T2> res = new View<T2>(new Flattenation(plan));
		return res;
	}

	public PlanNode getPlan() {
		return plan;
	}

	public void setPlan(PlanNode plan) {
		this.plan = plan;
	}

	public View<T> clone() {
		try {
			return (View<T>) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public Queryable<T> union(Queryable<T>... args) {
		Union u = new Union();
		u.addChildren(this.getPlan());
		for (Queryable<T> arg : args) {
			u.addChildren(arg.getPlan());
		}
		View<T> res = new View<T>(u);
		return res;
	}

	@Override
	public <K> VTable<K, T> groupBy(Path path) {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public <S> S reduce(ReduceFn<S, T> fn) {
		// TODO Auto-generated method stub
		return null;
	}
}
